/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package pedigreereconstruction;

import distribution.RelationshipDistribution;
import java.io.*;
import java.util.*;
import java.util.logging.*;
import org.apache.commons.cli.*;
import relationships.ProbabilisticRelationship;
import relationships.Relationship;
import relationships.RelationshipTypes;

/**
 *
 * @author arthiramachandran
 */
public class PedigreeReconstructionNoRemove {

    public static void combinePedigreesWithCommonIndividualsNoRemove(
            HashSet<HashSet<Pedigree>> pedigreesWithCommonIndividuals,
            boolean VERBOSE) {
        int i = 0;
        for (Iterator<HashSet<Pedigree>> it =
                pedigreesWithCommonIndividuals.iterator();
                it.hasNext();) {
            HashSet<Pedigree> toCombinePedigrees =
                    (HashSet<Pedigree>) it.next();
            try {
                i++;
                System.out.println("Combining: " + i);
                combinePedigrees(toCombinePedigrees, VERBOSE);
            } catch (Exception ex) {
                Logger.getLogger(PedigreeReconstructionNoRemove.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

        // remove pedigrees that are sub-pedigrees of others
        //removeSubPedigrees();
    }

    private static void combinePedigrees(
            HashSet<Pedigree> toCombinePedigrees, boolean VERBOSE) throws
            Exception {
        ProbabilisticPedigree newCombinedPedigree = new ProbabilisticPedigree();
        boolean changed = false;
        // TODO: prob not necessary
        //ArrayList<String> toCombinePedigreesOrderedKeys =
        //        new ArrayList(toCombinePedigrees.keySet());
        //Collections.shuffle(toCombinePedigreesOrderedKeys);
        // TODO: change to proper sort 
        // used to make sure the smaller pedigree is added to the bigger pedigree
        /*Collections.sort(toCombinePedigreesOrderedKeys, new Comparator<String>() {
        
        @Override
        public int compare(String a, String b) {
        return a.length() - b.length();
        }
        });
         * 
         */
        //ArrayList<ProbabilisticPedigree> toCombinePedigreesOrdered =
        //        orderBySize(toCombinePedigrees);
        System.out.println("------------------------------------");
        //for (String key : toCombinePedigreesOrderedKeys) {
        for (Iterator<Pedigree> it =
                toCombinePedigrees.iterator();
                it.hasNext();) {
            ProbabilisticPedigree p = (ProbabilisticPedigree) it.next();
            //ProbabilisticPedigree p =
            //        (ProbabilisticPedigree) toCombinePedigrees.get(key);
            System.out.println("Adding: " + p.likelihood
                    + " (L(null)= " + p.logLikelihoodOfNull()
                    + ", L(over null) " + p.logModelLikelihoodOverNull()
                    + " )\n" + p.printGraph());
            //System.out.println("to: " + newCombinedPedigree.printGraph());
            //for (ProbabilisticPedigree p : toCombinePedigreesOrdered) {
            //for (Iterator<Pedigree> it = toCombinePedigrees.values().iterator(); it.hasNext();) {

            //  ProbabilisticPedigree p = (ProbabilisticPedigree) it.next();
            ProbabilisticPedigree potentialNewPedigree =
                    new ProbabilisticPedigree();

            // copy over newCombinedPedigree to tempP
            for (Iterator<Relationship> r_iterator =
                    newCombinedPedigree.getRelationshipIterator(); r_iterator.hasNext();) {
                ProbabilisticRelationship r =
                        (ProbabilisticRelationship) r_iterator.next();
                if (!r.containsMissing()) {
                    potentialNewPedigree.addRelationship(r, r.isRelationshipSymmetric());
                }
            }

            // add the relationships in p
            // first add those relationships which don't have missing individuals
            for (Iterator<Relationship> r_iterator = p.getRelationshipIterator(); r_iterator.hasNext();) {
                ProbabilisticRelationship r =
                        (ProbabilisticRelationship) r_iterator.next();
                if (!r.containsMissing()) {
                    if (!potentialNewPedigree.containsRelationship(r)) {
                        potentialNewPedigree.addRelationship(r, r.isRelationshipSymmetric());
                    }
                }
            }

            // TODO: remove - debugging statement
            for (Iterator<Relationship> r_iterator =
                    potentialNewPedigree.getRelationshipIterator(); r_iterator.hasNext();) {
                ProbabilisticRelationship r =
                        (ProbabilisticRelationship) r_iterator.next();
                if (r.containsMissing()) {
                    System.out.println("!!" + r);
                }
            }

            /*
            if (!potentialNewPedigree.isConsistent()) {
            System.out.println("Inconsistent_1!!");
            System.out.println(potentialNewPedigree.printGraph());
            //System.out.println(p.printGraph());
            continue;
            }
             * 
             */
            ProbabilisticPedigree temp2 =
                    new ProbabilisticPedigree(potentialNewPedigree);
            if (!temp2.equals(potentialNewPedigree)) {
                throw new Exception("Incorrect Copy");
            }
            HashMap<Individual, Boolean> done =
                    new HashMap<Individual, Boolean>();
            for (Individual i1 : potentialNewPedigree.getIndividuals()) {
                done.put(i1, Boolean.TRUE);
                for (Individual i2 : potentialNewPedigree.getIndividuals()) {
                    /* if relationship does not exist between i1 and i2, 
                     * add appropriate relationship
                     */
                    //System.out.println("Doing??? " + i1 + " " + i2);

                    if (i1.equals(i2) || done.containsKey(i2)) {
                        continue;
                    }
                    Relationship r = temp2.getRelationship(i1, i2);
                    if (r == null) {
                        r = temp2.getRelationship(i2, i1);
                    }
                    if (r == null) {
                        //System.out.println(potentialNewPedigree.printGraph());
                        //System.out.println("here" + temp2.getIndividuals());
                        //System.out.println("here" + potentialNewPedigree.getIndividuals());
                        temp2.addConnectingRelationship(i1, i2, VERBOSE);
                    } else if (r.relationshipType.equals(PedigreeReconstruction.unrelated)) {
                        //System.out.println("here2" + temp2.getIndividuals());
                        temp2.addConnectingRelationship(i1, i2, VERBOSE);
                    }
                }
            }

            potentialNewPedigree = temp2;

            // then add the relationships with missing indiv.
            for (Iterator<Relationship> r_iterator =
                    newCombinedPedigree.getRelationshipIterator(); r_iterator.hasNext();) {
                ProbabilisticRelationship r =
                        (ProbabilisticRelationship) r_iterator.next();
                if (!r.containsMissing()) {
                    continue;
                }
                boolean relationExists =
                        (potentialNewPedigree.getRelationship(r.individual1, r.individual2) != null)
                        || (potentialNewPedigree.getRelationship(r.individual2, r.individual1) != null);
                if (!relationExists
                        && r.relationshipType.equals(PedigreeReconstruction.parent_child)) {
                    //&& r.containsMissing()) {
                    // if non-missing person doesn't have two parents
                    Individual nonMissing = r.getNonMissingIndividual();
                    // if its a parent-child relationships and there isnt already enough parent-child for that indiv
                    if (potentialNewPedigree.containsIndividual(nonMissing)) {
                        if (potentialNewPedigree.getParents(nonMissing).size() < 2) {
                            potentialNewPedigree.addRelationship(r, r.isRelationshipSymmetric());
                        }
                    } else {
                        throw new Exception("Incorrect execution path");
                    }
                }

            }

            /*
            if (!potentialNewPedigree.isConsistent()) {
            System.out.println("Inconsistent_2!!");
            System.out.println(potentialNewPedigree.printGraph());
            //System.out.println(p.printGraph());
            continue;
            }
             * 
             */
            // add the siblings parents
            // if they are siblings; add the same missing parents
            ArrayList<ProbabilisticRelationship> rs =
                    new ArrayList<ProbabilisticRelationship>();
            for (Iterator<Relationship> r_iterator =
                    potentialNewPedigree.getRelationshipIterator(); r_iterator.hasNext();) {
                ProbabilisticRelationship r =
                        (ProbabilisticRelationship) r_iterator.next();
                if (!r.relationshipType.equals(PedigreeReconstruction.siblings)) {
                    continue;
                }
                if (potentialNewPedigree.getParents(r.individual1).size() == 2
                        && potentialNewPedigree.getParents(r.individual2).size() == 2) {
                    continue;
                }
                if (potentialNewPedigree.getParents(r.individual1).size() == 2) {
                    // copy the missing parents from i1 to i2
                    for (Individual parent : potentialNewPedigree.getParents(r.individual1)) {
                        if (parent.isMissing()) {
                            ProbabilisticRelationship r_toadd =
                                    new ProbabilisticRelationship(parent, r.individual2, -1, -1,
                                    -1, PedigreeReconstruction.parent_child);
                            r_toadd.setRelationshipDistribution(
                                    PedigreeReconstruction.knownPairwiseDistributions.get(PedigreeReconstruction.parent_child));
                            // should never be going into this
                            if (potentialNewPedigree.containsRelationship(r_toadd)) {
                                continue;
                                //throw (new Exception("Erroneous execution path"));
                            }
                            r_toadd.calculateRelationshipLikelihood();
                            rs.add(r_toadd);
                        }
                    }
                }
                if (potentialNewPedigree.getParents(r.individual2).size() == 2) {
                    // copy the parents from i2 to i1
                    for (Individual parent : potentialNewPedigree.getParents(r.individual2)) {
                        if (parent.isMissing()) {
                            ProbabilisticRelationship r_toadd =
                                    new ProbabilisticRelationship(parent, r.individual1, -1, -1,
                                    -1, PedigreeReconstruction.parent_child);
                            r_toadd.setRelationshipDistribution(
                                    PedigreeReconstruction.knownPairwiseDistributions.get(PedigreeReconstruction.parent_child));
                            // should never be going into this
                            if (potentialNewPedigree.containsRelationship(r_toadd)) {
                                continue;
                                //throw (new Exception("Erroneous execution path " + r_toadd
                                //        + "\t" + newCombinedPedigree.printGraph()));
                            }
                            r_toadd.calculateRelationshipLikelihood();
                            rs.add(r_toadd);
                            //newCombinedPedigree.addRelationship(r_toadd, r_toadd.isRelationshipSymmetric());
                        }
                    }
                }
            }
            for (ProbabilisticRelationship r_toadd : rs) {
                potentialNewPedigree.addRelationship(r_toadd, r_toadd.isRelationshipSymmetric());
            }

            // add missing relationships in p
            for (Iterator<Relationship> r_iterator = p.getRelationshipIterator(); r_iterator.hasNext();) {
                ProbabilisticRelationship r =
                        (ProbabilisticRelationship) r_iterator.next();
                if (!r.containsMissing()) {
                    continue;
                }
                boolean relationExists =
                        (potentialNewPedigree.getRelationship(r.individual1, r.individual2) != null)
                        || (potentialNewPedigree.getRelationship(r.individual2, r.individual1) != null);
                if (!relationExists
                        && r.relationshipType.equals(PedigreeReconstruction.parent_child)) {
                    //&& r.containsMissing()) {
                    Individual nonMissing = r.getNonMissingIndividual();
                    if (potentialNewPedigree.containsIndividual(nonMissing)) {
                        if (potentialNewPedigree.getParents(nonMissing).size() < 2) {
                            potentialNewPedigree.addRelationship(r, r.isRelationshipSymmetric());
                        }
                    } else {
                        throw new Exception("Incorrect execution path");
                    }

                }

            }


            /*
            if (!potentialNewPedigree.isConsistent()) {
            System.out.println("Inconsistent_3!!");
            System.out.println(potentialNewPedigree.printGraph());
            //System.out.println(p.printGraph());
            continue;
            }
             * 
             */

            if (potentialNewPedigree.equals(newCombinedPedigree)) { // nothing's been added
                System.out.println("Nothing new added");
                //System.out.println("ISSUE!!");
                continue;
            }

            // TODO: need to combine all trios with the same relationship edge at once
            // TODO: add remaining connections between other people
            // copy tempP to temp2
            /*ProbabilisticPedigree temp2 = new ProbabilisticPedigree();
            for (Iterator<Relationship> r_iterator =
            potentialNewPedigree.getRelationshipIterator(); r_iterator.hasNext();) {
            ProbabilisticRelationship r =
            (ProbabilisticRelationship) r_iterator.next();
            temp2.addRelationship(r, r.isRelationshipSymmetric());
            }
             */

            double alpha =
                    PedigreeReconstruction.logFalseDiscoveryRate(potentialNewPedigree);
            System.out.println("FDR: " + alpha);// + " " + tempP.getNumberOfRelationships());

            if (!potentialNewPedigree.isConsistent()) {
                System.out.println("Inconsistent after adding connecting relationships");
                System.out.println(potentialNewPedigree.printGraph());
                continue;
            }
            potentialNewPedigree.computeLogProbability();
            //System.out.println("Consistent " + p.likelihood + "\n --\n");
            //System.out.println(tempP.printGraph());
            // TODO: need to check this

            //System.out.println(tempP.getNumberOfIndividuals() + " " + tempP.getNumberOfRelationships());
            try {

                if (newCombinedPedigree.getNumberOfIndividuals() == 0) { // always add in the first one without checking alpha's and such
                    for (Iterator<Relationship> r_iterator =
                            p.getRelationshipIterator(); r_iterator.hasNext();) {
                        ProbabilisticRelationship r =
                                (ProbabilisticRelationship) r_iterator.next();
                        //System.out.println("removing : " + r + " " + pedigrees.get(r) + "\n" + p + "\n" + p.printGraph());

                        //boolean remove =
                        //      pedigrees.get(r).remove(p);
                        /*
                        if (!remove) {
                        throw new Exception(
                        "Unable to remove pedigree with individuals" + p.getIndividuals());
                        }
                         * 
                         */
                    }
                    newCombinedPedigree = potentialNewPedigree;
                    changed = true;
                } else {
                    /*double alpha =
                    PedigreeReconstruction.logFalseDiscoveryRate(potentialNewPedigree);
                    System.out.println("FDR: " + alpha);// + " " + tempP.getNumberOfRelationships());
                     * 
                     */
                    System.out.println("Likelihoods: " + potentialNewPedigree.likelihood + " "
                            + newCombinedPedigree.likelihood + " "
                            + p.likelihood);
                    System.out.println("# Relationships: " + potentialNewPedigree.getNumberOfRelationships() + " "
                            + newCombinedPedigree.getNumberOfRelationships() + " "
                            + p.getNumberOfRelationships());
                    System.out.println(potentialNewPedigree.logModelLikelihoodOverNull() + " vs. "
                            + newCombinedPedigree.logModelLikelihoodOverNull() + " + "
                            + p.logModelLikelihoodOverNull() + " (= "
                            + (newCombinedPedigree.logModelLikelihoodOverNull() + p.logModelLikelihoodOverNull()) + ")");
                    System.out.println("FDR: " + alpha);
                    if ((potentialNewPedigree.logModelLikelihoodOverNull() > alpha)
                            /*&& (potentialNewPedigree.likelihood 
                            >= (newCombinedPedigree.likelihood + p.likelihood - penalty))) {*/
                            /*&& (potentialNewPedigree.logModelLikelihoodOverNull()
                            >= (newCombinedPedigree.logModelLikelihoodOverNull()
                            + p.logModelLikelihoodOverNull() - penalty))) {*/
                            && (potentialNewPedigree.logModelLikelihoodOverNull()
                            >= (newCombinedPedigree.logModelLikelihoodOverNull() - .1))
                            && (potentialNewPedigree.logModelLikelihoodOverNull()
                            >= (p.logModelLikelihoodOverNull() - .1))) {
                        //(potentialNewPedigree.likelihood >= (newCombinedPedigree.likelihood + p.likelihood - 10e-6))) {
                        //true) {
                        // adding in some fleixbility - don't want to deal with rounding errors
                        // TODO: better way to address this

                        //if ((tempP.logModelLikelihoodOverNull()
                        //        > (newCombinedPedigree.logModelLikelihoodOverNull() /*+ p.logModelLikelihoodOverNull())*/
                        //        && (tempP.logModelLikelihoodOverNull() > alpha)) {

                        System.out.println("probs ok");
                        System.out.println(potentialNewPedigree.logModelLikelihoodOverNull() + " vs. "
                                + newCombinedPedigree.logModelLikelihoodOverNull() + " + "
                                + p.logModelLikelihoodOverNull() + " (= "
                                + (newCombinedPedigree.logModelLikelihoodOverNull() + p.logModelLikelihoodOverNull()) + ")");
                        System.out.println("Partially combined: " + potentialNewPedigree.printGraph());
                        System.out.println("(old) combined: " + newCombinedPedigree.printGraph());
                        newCombinedPedigree = potentialNewPedigree;
                        changed = true;

                    } else {
                        if (potentialNewPedigree.logModelLikelihoodOverNull() <= alpha) {
                            System.out.println("probs not ok; not above fp threshold (" + alpha + "); not combining");
                            System.out.println("Combined");
                            for (Iterator<Relationship> r_iterator =
                                    potentialNewPedigree.getRelationshipIterator();
                                    r_iterator.hasNext();) {
                                ProbabilisticRelationship r =
                                        (ProbabilisticRelationship) r_iterator.next();
                                System.out.println(r.toVerboseString()
                                        + " null: " + r.calculateNullLikelihood()
                                        + " over null: " + (r.getRelationshipLikelihood() / r.calculateNullLikelihood()));
                            }

                        } else if ((potentialNewPedigree.logModelLikelihoodOverNull()
                                < (newCombinedPedigree.logModelLikelihoodOverNull() - .1))
                                || (potentialNewPedigree.logModelLikelihoodOverNull()
                                < (p.logModelLikelihoodOverNull() - .1))) {
                            System.out.println("probs not ok; combined likelihood not enough; not combining");

                            System.out.println("Combined");
                            for (Iterator<Relationship> r_iterator =
                                    potentialNewPedigree.getRelationshipIterator();
                                    r_iterator.hasNext();) {
                                ProbabilisticRelationship r =
                                        (ProbabilisticRelationship) r_iterator.next();
                                System.out.println(r.toVerboseString()
                                        + " null: " + r.calculateNullLikelihood()
                                        + " over null: " + (r.getRelationshipLikelihood() / r.calculateNullLikelihood()));
                            }
                            System.out.println("Component1");
                            for (Iterator<Relationship> r_iterator =
                                    newCombinedPedigree.getRelationshipIterator();
                                    r_iterator.hasNext();) {
                                ProbabilisticRelationship r =
                                        (ProbabilisticRelationship) r_iterator.next();
                                System.out.println(r.toVerboseString()
                                        + " null: " + r.calculateNullLikelihood()
                                        + " over null: " + (r.getRelationshipLikelihood() / r.calculateNullLikelihood()));
                            }
                            System.out.println("Component2");
                            for (Iterator<Relationship> r_iterator =
                                    p.getRelationshipIterator();
                                    r_iterator.hasNext();) {
                                ProbabilisticRelationship r =
                                        (ProbabilisticRelationship) r_iterator.next();
                                System.out.println(r.toVerboseString()
                                        + " null: " + r.calculateNullLikelihood()
                                        + " over null: " + (r.getRelationshipLikelihood() / r.calculateNullLikelihood()));
                            }

                            System.out.println(potentialNewPedigree.likelihood + " vs. "
                                    + newCombinedPedigree.likelihood + " + "
                                    + p.likelihood + " (= "
                                    + (newCombinedPedigree.likelihood + p.likelihood) + ")");
                            //System.out.println("Adding: " + potentialNewPedigree.getIndividuals());
                            //System.out.println("Partially combined: " + p.getIndividuals());
                        } else {
                            System.out.println("probs not ok; weird!; not combining");
                        }
                        //System.out.println("(old) combined: " + newCombinedPedigree.printGraph());
                    }
                }
            } catch (Exception ex) {
                Logger.getLogger(PedigreeReconstruction.class.getName()).log(
                        Level.SEVERE, null, ex);
            }

        }

        // TODO: do we need this?
        if (changed && newCombinedPedigree.getNumberOfIndividuals() > 0) {
            for (Iterator<Relationship> r_iterator =
                    newCombinedPedigree.getRelationshipIterator(); r_iterator.hasNext();) {
                ProbabilisticRelationship r =
                        (ProbabilisticRelationship) r_iterator.next();
                TreeSet<ProbabilisticPedigree> prev =
                        PedigreeReconstruction.pedigrees.get(r);
                if (prev == null) {
                    prev = new TreeSet<ProbabilisticPedigree>();
                }
                //if (newCombinedPedigree.likelihood >= 0) {
                //    System.out.println(newCombinedPedigree.printGraph());
                //}
                //newCombinedPedigree.computeLogProbability();
                prev.add(newCombinedPedigree);
                //System.out.println("___Combined Pedigree\n" + newCombinedPedigree.printGraph());
                PedigreeReconstruction.pedigrees.put(r, prev);
            }
        }
    }

    private static void addToAlreadyAttempted(ProbabilisticPedigree p1,
            ProbabilisticPedigree p2) {
        //System.out.println("!!!");
        //System.out.println("Adding to attempted: " + p1.printGraph() + " and " + p2.printGraph());

        TreeSet<ProbabilisticPedigree> list =
                PedigreeReconstruction.alreadyAttemptedCombination.get(p1);
        if (list == null) {
            list = new TreeSet<ProbabilisticPedigree>();
        }
        list.add(p2);
        PedigreeReconstruction.alreadyAttemptedCombination.put(p1, list);
        list = null;
        list =
                PedigreeReconstruction.alreadyAttemptedCombination.get(p2.getNonMissingRelationships());
        if (list == null) {
            list = new TreeSet<ProbabilisticPedigree>();
        }
        list.add(p1);
        PedigreeReconstruction.alreadyAttemptedCombination.put(p2, list);
    }
}
